1   /*
2    * Copyright (C) 2012 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5    * in compliance with the License. You may obtain a copy of the License at
6    *
7    * http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software distributed under the License
10   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11   * or implied. See the License for the specific language governing permissions and limitations under
12   * the License.
13   */
14  
15  package com.google.common.io;
16  
17  import static com.google.common.base.Preconditions.checkNotNull;
18  import static com.google.common.base.Preconditions.checkPositionIndexes;
19  
20  import com.google.common.annotations.GwtCompatible;
21  import com.google.common.annotations.GwtIncompatible;
22  
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.io.OutputStream;
26  import java.io.Reader;
27  import java.io.Writer;
28  
29  /**
30   * Provides simple GWT-compatible substitutes for {@code InputStream}, {@code OutputStream},
31   * {@code Reader}, and {@code Writer} so that {@code BaseEncoding} can use streaming implementations
32   * while remaining GWT-compatible.
33   *
34   * @author Louis Wasserman
35   */
36  @GwtCompatible(emulated = true)
37  final class GwtWorkarounds {
38    private GwtWorkarounds() {}
39  
40    /**
41     * A GWT-compatible substitute for a {@code Reader}.
42     */
43    interface CharInput {
44      int read() throws IOException;
45      void close() throws IOException;
46    }
47  
48    /**
49     * Views a {@code Reader} as a {@code CharInput}.
50     */
51    @GwtIncompatible("Reader")
52    static CharInput asCharInput(final Reader reader) {
53      checkNotNull(reader);
54      return new CharInput() {
55        @Override
56        public int read() throws IOException {
57          return reader.read();
58        }
59  
60        @Override
61        public void close() throws IOException {
62          reader.close();
63        }
64      };
65    }
66  
67    /**
68     * Views a {@code CharSequence} as a {@code CharInput}.
69     */
70    static CharInput asCharInput(final CharSequence chars) {
71      checkNotNull(chars);
72      return new CharInput() {
73        int index = 0;
74  
75        @Override
76        public int read() {
77          if (index < chars.length()) {
78            return chars.charAt(index++);
79          } else {
80            return -1;
81          }
82        }
83  
84        @Override
85        public void close() {
86          index = chars.length();
87        }
88      };
89    }
90  
91    /**
92     * A GWT-compatible substitute for an {@code InputStream}.
93     */
94    interface ByteInput {
95      int read() throws IOException;
96      void close() throws IOException;
97    }
98  
99    /**
100    * Views a {@code ByteInput} as an {@code InputStream}.
101    */
102   @GwtIncompatible("InputStream")
103   static InputStream asInputStream(final ByteInput input) {
104     checkNotNull(input);
105     return new InputStream() {
106       @Override
107       public int read() throws IOException {
108         return input.read();
109       }
110 
111       @Override
112       public int read(byte[] b, int off, int len) throws IOException {
113         checkNotNull(b);
114         checkPositionIndexes(off, off + len, b.length);
115         if (len == 0) {
116           return 0;
117         }
118         int firstByte = read();
119         if (firstByte == -1) {
120           return -1;
121         }
122         b[off] = (byte) firstByte;
123         for (int dst = 1; dst < len; dst++) {
124           int readByte = read();
125           if (readByte == -1) {
126             return dst;
127           }
128           b[off + dst] = (byte) readByte;
129         }
130         return len;
131       }
132 
133       @Override
134       public void close() throws IOException {
135         input.close();
136       }
137     };
138   }
139 
140   /**
141    * A GWT-compatible substitute for an {@code OutputStream}.
142    */
143   interface ByteOutput {
144     void write(byte b) throws IOException;
145     void flush() throws IOException;
146     void close() throws IOException;
147   }
148 
149   /**
150    * Views a {@code ByteOutput} as an {@code OutputStream}.
151    */
152   @GwtIncompatible("OutputStream")
153   static OutputStream asOutputStream(final ByteOutput output) {
154     checkNotNull(output);
155     return new OutputStream() {
156       @Override
157       public void write(int b) throws IOException {
158         output.write((byte) b);
159       }
160 
161       @Override
162       public void flush() throws IOException {
163         output.flush();
164       }
165 
166       @Override
167       public void close() throws IOException {
168         output.close();
169       }
170     };
171   }
172 
173   /**
174    * A GWT-compatible substitute for a {@code Writer}.
175    */
176   interface CharOutput {
177     void write(char c) throws IOException;
178     void flush() throws IOException;
179     void close() throws IOException;
180   }
181 
182   /**
183    * Views a {@code Writer} as a {@code CharOutput}.
184    */
185   @GwtIncompatible("Writer")
186   static CharOutput asCharOutput(final Writer writer) {
187     checkNotNull(writer);
188     return new CharOutput() {
189       @Override
190       public void write(char c) throws IOException {
191         writer.append(c);
192       }
193 
194       @Override
195       public void flush() throws IOException {
196         writer.flush();
197       }
198 
199       @Override
200       public void close() throws IOException {
201         writer.close();
202       }
203     };
204   }
205 
206   /**
207    * Returns a {@code CharOutput} whose {@code toString()} method can be used
208    * to get the combined output.
209    */
210   static CharOutput stringBuilderOutput(int initialSize) {
211     final StringBuilder builder = new StringBuilder(initialSize);
212     return new CharOutput() {
213 
214       @Override
215       public void write(char c) {
216         builder.append(c);
217       }
218 
219       @Override
220       public void flush() {}
221 
222       @Override
223       public void close() {}
224 
225       @Override
226       public String toString() {
227         return builder.toString();
228       }
229     };
230   }
231 }